#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#       start.py
#       
#       Copyright 2010 Hind <foxhind@gmail.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import sys
import math
import time
#import pdb

from loader_osm import generate_scene
from OsmData import LON, LAT, REF

global scene
oldtime = time.time()
scenter = (400, 300)
pitchbs = (-math.pi/2.0*0.9, math.pi/2.0*0.9)

def loaddata():
  global scene, lastX, lastY, player, keyboard
  lastX = -1
  lastY = -1
  if len(sys.argv) > 1:
    scene = generate_scene(sys.argv[1])
  else:
    sys.exit()
  keyboard = Keyboard()
  player = Player()
  player.camera.eye = [scene[0][1][0], scene[0][1][1], scene[0][0][1] + 100.0]
  player.camera.recalc()

class Keyboard():
  def __init__(self):
    self.up = False
    self.down = False
    self.left = False
    self.right = False
    self.space = False
    self.shift = 1.0

class Camera():
  def __init__(self):
    self.eye = [0.0, 0.0, 0.0]
    self.direct = [1.0, 0.0, 0.0]
    self.left = [0.0, 1.0, 0.0]
    self.center = [1.0, 0.0, 0.0]
    self.up = [0.0, 0.0, 1.0]
    self.yaw = 0.0
    self.pitch = 0.0
  def rotyaw(self, value):
    self.yaw += value
    if self.yaw > math.pi:
      self.yaw -= math.pi*2
    elif self.yaw < -math.pi:
      self.yaw += math.pi*2
    self.recalc()
  def rotpitch(self, value):
    self.pitch += value
    if self.pitch > pitchbs[1]:
      self.pitch = pitchbs[1]
    elif self.pitch < pitchbs[0]:
      self.pitch = pitchbs[0]
    self.recalc()
  def forward(self, value):
    self.eye[0] += self.direct[0]*value
    self.eye[1] += self.direct[1]*value
    self.eye[2] += self.direct[2]*value
    self.recalc()
  def strafe(self, value):
    self.eye[0] += self.left[0]*value
    self.eye[1] += self.left[1]*value
    self.eye[2] += self.left[2]*value
    self.recalc()
  def recalc(self):
    self.direct[0] = math.cos(self.yaw)*math.cos(self.pitch)
    self.direct[1] = math.sin(self.yaw)*math.cos(self.pitch)
    self.direct[2] = math.sin(self.pitch)
    self.left[0] = -math.sin(self.yaw)
    self.left[1] = math.cos(self.yaw)
    self.center[0] = self.eye[0] + self.direct[0]
    self.center[1] = self.eye[1] + self.direct[1]
    self.center[2] = self.eye[2] + self.direct[2]
  def move(self, vector):
    self.eye[0] += vector[0]
    self.eye[1] += vector[1]
    self.eye[2] += vector[2]
    self.recalc()

class Player():
  def __init__(self):
    self.speed = [0.0, 0.0, 0.0]
    self.maxspeed = 250.0
    self.camera = Camera()
  def accelerate(self, value, vector):
    self.speed[0] += value*vector[0]
    self.speed[1] += value*vector[1]
    self.speed[2] += value*vector[2]
    speed = math.sqrt(self.speed[0]*self.speed[0] + self.speed[1]*self.speed[1] + self.speed[2]*self.speed[2])
    coeff = speed / self.maxspeed
    if coeff > 1:
      self.speed[0] /= coeff
      self.speed[1] /= coeff
      self.speed[2] /= coeff
  def recalc(self, dt):
    self.camera.move((self.speed[0]*dt, self.speed[1]*dt, self.speed[2]*dt))
    if keyboard.space:
      self.speed[0] *= 0.7
      self.speed[1] *= 0.7
      self.speed[2] *= 0.7
    else:
      self.speed[0] *= 0.95
      self.speed[1] *= 0.95
      self.speed[2] *= 0.95

def InitGL(Width, Height):
  glClearColor(0.945, 0.933, 0.91, 0.0)
  glClearDepth(1.0)
  glDepthFunc(GL_LESS)
  glEnable(GL_DEPTH_TEST)
  glEnable(GL_LIGHTING)
  glEnable(GL_LIGHT0)
  glEnable(GL_COLOR_MATERIAL)
  glEnable(GL_BLEND)
  glLineWidth(1)
  glEnable(GL_LINE_SMOOTH)
  glShadeModel(GL_SMOOTH)
  glMatrixMode(GL_PROJECTION)
  glLoadIdentity()
  gluPerspective(90.0, float(Width)/float(Height), 1.0, 10000.0)
  glMatrixMode(GL_MODELVIEW)
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  createcache()
  glutSetCursor(GLUT_CURSOR_NONE)

def createcache():
  glNewList(1, GL_COMPILE)
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
  tobj = gluNewTess()
  gluTessCallback(tobj, GLU_TESS_BEGIN, glBegin)
  gluTessCallback(tobj, GLU_TESS_VERTEX, glVertex3fv)
  gluTessCallback(tobj, GLU_TESS_END, glEnd)
  for bld in scene:
    minh = bld[0][0]
    maxh = bld[0][1]
    lightcolor = (bld[0][2], bld[0][3], bld[0][4], bld[0][5])
    darkcolor = (lightcolor[0]*0.7, lightcolor[1]*0.7, lightcolor[2]*0.7, lightcolor[3])
    glColor4f(*darkcolor)
    gluTessBeginPolygon(tobj, None)
    gluTessBeginContour(tobj)
    for i in xrange(1, len(bld)):
      gluTessVertex(tobj, (bld[i][0], bld[i][1], minh), (bld[i][0], bld[i][1], minh))
    gluTessEndContour(tobj)
    gluTessEndPolygon(tobj)
    glColor4f(*lightcolor)
    gluTessBeginPolygon(tobj, None)
    gluTessBeginContour(tobj)
    for i in xrange(1, len(bld)):
      gluTessVertex(tobj, (bld[i][0], bld[i][1], maxh), (bld[i][0], bld[i][1], maxh))
    gluTessEndContour(tobj)
    gluTessEndPolygon(tobj)
    if (len(bld) > 2):
      glBegin(GL_QUAD_STRIP)
      glColor4f(*darkcolor)
      glVertex3f(bld[1][0], bld[1][1], minh)
      glColor4f(*lightcolor)
      glVertex3f(bld[1][0], bld[1][1], maxh)
      for i in xrange(2, len(bld)):
        glColor4f(*darkcolor)
        glVertex3f(bld[i][0], bld[i][1], minh)
        glColor4f(*lightcolor)
        glVertex3f(bld[i][0], bld[i][1], maxh)
      glEnd()
  glEndList()
  
  glNewList(2, GL_COMPILE)
  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
  for bld in scene:
    lightcolor = (bld[0][2], bld[0][3], bld[0][4], bld[0][5])
    darkcolor = (lightcolor[0]*0.7, lightcolor[1]*0.7, lightcolor[2]*0.7, lightcolor[3])
    glColor4f(*darkcolor)
    minh = bld[0][0]
    maxh = bld[0][1]
    glBegin(GL_LINE_STRIP)
    for i in xrange(1, len(bld)):
      glVertex3f(bld[i][0], bld[i][1], minh)
    glEnd()
    glBegin(GL_LINE_STRIP)
    for i in xrange(1, len(bld)):
      glVertex3f(bld[i][0], bld[i][1], maxh)
    glEnd()
    for i in xrange(1, len(bld)):
      glBegin(GL_LINES)
      glVertex3f(bld[i][0], bld[i][1], minh)
      glVertex3f(bld[i][0], bld[i][1], maxh)
      glEnd()
  glEndList()
  
  glNewList(3, GL_COMPILE)
  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
  glColor4f(0.58, 0.83, 1.0, 1.0)
  glDisable(GL_DEPTH_TEST);
  gluSphere(gluNewQuadric(), 1000.0, 24, 24);
  glEnable(GL_DEPTH_TEST);
  glEndList()

def ReSizeGLScene(Width, Height):
  if Height == 0: Height = 1
  glViewport(0, 0, Width, Height)
  glMatrixMode(GL_PROJECTION)
  glLoadIdentity()
  gluPerspective(90.0, float(Width)/float(Height), 1.0, 10000.0)
  glMatrixMode(GL_MODELVIEW)
  scenter = (Width/2, Height/2)

def DrawGLScene():
  global scene, oldtime, keyboard
  if keyboard.up:
    player.accelerate(10.0, player.camera.direct)
  if keyboard.down:
    player.accelerate(-10.0, player.camera.direct)
  if keyboard.left:
    player.accelerate(10.0, player.camera.left)
  if keyboard.right:
    player.accelerate(-10.0, player.camera.left)

  newtime = time.time()
  dt = newtime - oldtime
  oldtime = newtime
  player.recalc(dt)
  
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
  glLoadIdentity()
  glCallList(3)
  gluLookAt(player.camera.eye[0], player.camera.eye[1], player.camera.eye[2], player.camera.center[0], player.camera.center[1], player.camera.center[2], player.camera.up[0], player.camera.up[1], player.camera.up[2])
  glLightfv(GL_LIGHT0, GL_DIFFUSE, (1.0, 1.0, 1.0))
  glLightfv(GL_LIGHT0, GL_AMBIENT, (0.9, 0.9, 0.9))
  glLightfv(GL_LIGHT0, GL_POSITION, (0.3, 1.0, 1.0, 0.0))
  glCallList(1)
  glCallList(2)
  glutSwapBuffers()

def KeyPressed(*args):
  global keyboard
  arg = args[0]
  if arg=="\x1B": sys.exit()
  elif arg=="w":
    keyboard.up = True
  elif arg=="s":
    keyboard.down = True
  elif arg=="a":
    keyboard.left = True
  elif arg=="d":
    keyboard.right = True
  elif arg==" ":
    keyboard.space = True

def KeyUpped(*args):
  global keyboard
  arg = args[0]
  if arg=="w":
    keyboard.up = False
  elif arg=="s":
    keyboard.down = False
  elif arg=="a":
    keyboard.left = False
  elif arg=="d":
    keyboard.right = False
  elif arg==" ":
    keyboard.space = False

def SKeyPressed(*args):
  global keyboard
  arg = args[0]
  if arg==GLUT_KEY_UP:
    keyboard.up = True
  elif arg==GLUT_KEY_DOWN:
    keyboard.down = True
  elif arg==GLUT_KEY_LEFT:
    keyboard.left = True
  elif arg==GLUT_KEY_RIGHT:
    keyboard.right = True

def SKeyUpped(*args):
  global keyboard
  arg = args[0]
  if arg==GLUT_KEY_UP:
    keyboard.up = False
  elif arg==GLUT_KEY_DOWN:
    keyboard.down = False
  elif arg==GLUT_KEY_LEFT:
    keyboard.left = False
  elif arg==GLUT_KEY_RIGHT:
    keyboard.right = False

def mouseFunc(x, y):
  global lastX, lastY
  #glutWarpPointer(*scenter)
  if lastX == -1:
    lastX = x
    lastY = y
  player.camera.rotyaw((lastX - x) * 0.01)
  player.camera.rotpitch((lastY - y) * 0.005)
  lastX = x
  lastY = y
  glutPostRedisplay()

def main():
  loaddata()
  glutInit(sys.argv)
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE)
  glutInitWindowSize(800, 600)
  glutInitWindowPosition(0, 0)
  glutCreateWindow("OsmWalker")
  glutDisplayFunc(DrawGLScene)
  glutIdleFunc(DrawGLScene)
  glutReshapeFunc(ReSizeGLScene)
  glutKeyboardFunc(KeyPressed)
  glutKeyboardUpFunc(KeyUpped)
  glutSpecialFunc(SKeyPressed)
  glutSpecialUpFunc(SKeyUpped)
  InitGL(800, 600)
  glutPassiveMotionFunc(mouseFunc)
  glutMainLoop()

main()
